﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/

using System;
using System.Linq;

namespace CSharpKit.Numerics.LinearAlgebra
{
    // Vector.Arithmetic
    partial class Vector<T>
    {

        #region Negate - 取反

        public Vector<T> Negate()
        {
            var retrunVector = Builder.SameAs(this);
            DoNegate(retrunVector);
            return retrunVector;
        }

        protected virtual void DoNegate(Vector<T> result)
        {
            Map(x => NumOperators.Negate(x), result, Zeros.AllowSkip);
        }

        #endregion


        #region Add(+)

        public Vector<T> Add(T scalar)
        {
            if (scalar.Equals(Zero))
            {
                return Clone();
            }

            var result = Builder.SameAs(this);
            DoAdd(scalar, result);
            return result;
        }
        public void Add(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            if (scalar.Equals(Zero))
            {
                CopyTo(result);
                return;
            }

            DoAdd(scalar, result);
        }

        public Vector<T> Add(Vector<T> other)
        {
            if (Count != other.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));
            }

            var result = Builder.SameAs(this, other);
            DoAdd(other, result);
            return result;
        }
        public void Add(Vector<T> other, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoAdd(other, result);
        }

        protected virtual void DoAdd(T scalar, Vector<T> result)
        {
            Map(x => NumOperators.Add(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual void DoAdd(Vector<T> other, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Add(x, y), other, result, Zeros.AllowSkip);
        }

        #endregion

        #region Subtract(-)

        public Vector<T> Subtract(T scalar)
        {
            if (scalar.Equals(Zero))
            {
                return Clone();
            }

            var result = Builder.SameAs(this);
            DoSubtract(scalar, result);
            return result;
        }
        public void Subtract(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            if (scalar.Equals(Zero))
            {
                CopyTo(result);
                return;
            }

            DoSubtract(scalar, result);
        }

        /// <summary>
        /// scalar - vector
        /// </summary>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public Vector<T> SubtractFrom(T scalar)
        {
            var result = Builder.SameAs(this);
            DoSubtractFrom(scalar, result);
            return result;
        }
        public void SubtractFrom(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoSubtractFrom(scalar, result);
        }

        public Vector<T> Subtract(Vector<T> other)
        {
            if (Count != other.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));
            }

            var result = Builder.SameAs(this, other);
            DoSubtract(other, result);
            return result;
        }
        public void Subtract(Vector<T> other, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoSubtract(other, result);
        }

        protected virtual void DoSubtract(T scalar, Vector<T> result)
        {
            Map(x => NumOperators.Subtract(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual void DoSubtractFrom(T scalar, Vector<T> result)
        {
            DoNegate(result);
            result.DoAdd(scalar, result);
        }
        protected virtual void DoSubtract(Vector<T> other, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Subtract(x, y), other, result, Zeros.AllowSkip);
        }

        #endregion

        #region Multiply(*)

        public Vector<T> Multiply(T scalar)
        {
            if (scalar.Equals(One))
            {
                return Clone();
            }

            if (scalar.Equals(Zero))
            {
                return Builder.SameAs(this);
            }

            var result = Builder.SameAs(this);
            DoMultiply(scalar, result);
            return result;
        }
        public void Multiply(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            if (scalar.Equals(One))
            {
                CopyTo(result);
                return;
            }

            if (scalar.Equals(Zero))
            {
                result.Clear();
                return;
            }

            DoMultiply(scalar, result);
        }

        public T Multiply(Vector<T> other)
        {
            if (Count != other.Count)
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));

            return DoDotProduct(other);
        }


        public T DotProduct(Vector<T> other)
        {
            if (Count != other.Count)
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));

            return DoDotProduct(other);
        }
        public T ConjugateDotProduct(Vector<T> other)
        {
            if (Count != other.Count) throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));

            return DoConjugateDotProduct(other);
        }

        protected virtual void DoMultiply(T scalar, Vector<T> result)
        {
            Map(x => NumOperators.Multiply(x, scalar), result, Zeros.AllowSkip);
        }
        protected virtual T DoDotProduct(Vector<T> other)
        {
            var dot = Zero;

            for (var i = 0; i < Count; i++)
            {
                dot = NumOperators.Add(dot, NumOperators.Multiply(this[i], other[i]));
            }

            return dot;
        }
        protected virtual T DoConjugateDotProduct(Vector<T> other)
        {
            return DoDotProduct(other);
        }

        #endregion

        #region Divide(/)

        public Vector<T> Divide(T divisor)
        {
            if (divisor.Equals(One))
            {
                return Clone();
            }

            var result = Builder.SameAs(this);
            DoDivide(divisor, result);
            return result;
        }
        public void Divide(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            if (scalar.Equals(One))
            {
                CopyTo(result);
                return;
            }

            DoDivide(scalar, result);
        }

        public Vector<T> DivideByThis(T dividend)
        {
            var result = Builder.SameAs(this);
            DoDivideByThis(dividend, result);
            return result;
        }
        public void DivideByThis(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoDivideByThis(scalar, result);
        }

        protected virtual void DoDivide(T divisor, Vector<T> result)
        {
            Map(x => NumOperators.Divide(x, divisor), result, Zeros.AllowSkip);
        }
        protected virtual void DoDivideByThis(T dividend, Vector<T> result)
        {
            Map(x => NumOperators.Divide(dividend, x), result, Zeros.AllowSkip);
        }

        public Vector<T> PointwiseDivide(Vector<T> divisor)
        {
            if (Count != divisor.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(divisor));
            }

            var result = Builder.SameAs(this, divisor);
            DoPointwiseDivide(divisor, result);
            return result;
        }

        #endregion

        #region Modulo(%)

        public Vector<T> Modulus(T divisor)
        {
            var result = Builder.SameAs(this);
            DoModulus(divisor, result);
            return result;
        }
        public void Modulus(T divisor, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoModulus(divisor, result);
        }
        public Vector<T> ModulusByThis(T dividend)
        {
            var result = Builder.SameAs(this);
            DoModulusByThis(dividend, result);
            return result;
        }
        public Vector<T> Modulus(Vector<T> divisor)
        {
            if (Count != divisor.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(divisor));
            }

            var result = Builder.SameAs(this, divisor);
            DoModulus(divisor, result);
            return result;
        }


        public Vector<T> Remainder(T divisor)
        {
            var result = Builder.SameAs(this);
            DoRemainder(divisor, result);
            return result;
        }
        public Vector<T> RemainderByThis(T dividend)
        {
            var result = Builder.SameAs(this);
            DoRemainderByThis(dividend, result);
            return result;
        }
        public Vector<T> PointwiseRemainder(Vector<T> divisor)
        {
            if (Count != divisor.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(divisor));
            }

            var result = Builder.SameAs(this, divisor);
            DoRemainder(divisor, result);
            return result;
        }


        protected virtual void DoModulus(T divisor, Vector<T> result)
        {
            Map(x => NumOperators.Modulo(x, divisor), result, Zeros.AllowSkip);
        }
        protected virtual void DoModulusByThis(T dividend, Vector<T> result)
        {
            Map(x => NumOperators.Modulo(dividend, x), result, Zeros.AllowSkip);
        }
        protected virtual void DoModulus(Vector<T> divisor, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Modulo(x, y), divisor, result, Zeros.AllowSkip);
        }

        protected virtual void DoRemainder(T divisor, Vector<T> result)
        {
            Map(x => NumOperators.Modulo(x, divisor), result, Zeros.AllowSkip);
        }
        protected virtual void DoRemainderByThis(T dividend, Vector<T> result)
        {
            Map(x => NumOperators.Modulo(dividend, x), result, Zeros.AllowSkip);
        }
        protected virtual void DoRemainder(Vector<T> divisor, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Modulo(x, y), divisor, result, Zeros.AllowSkip);
        }

        #endregion


        #region OuterProduct - 外积

        public Matrix<T> OuterProduct(Vector<T> other)
        {
            var matrix = Matrix<T>.Builder.SameAs(this, Count, other.Count);
            DoOuterProduct(other, matrix);
            return matrix;
        }
        public void OuterProduct(Vector<T> other, Matrix<T> result)
        {
            if (Count != result.RowCount || other.Count != result.ColumnCount)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentMatrixDimensions, nameof(result));
            }

            DoOuterProduct(other, result);
        }

        /// <summary>
        /// 线性代数外积：
        /// Computes the outer product M[i,j] = u[i]*v[j] of this and another vector and stores the result in the result matrix.
        /// </summary>
        /// <param name="other">The other vector</param>
        /// <param name="result">The matrix to store the result of the product.</param>
        protected void DoOuterProduct(Vector<T> other, Matrix<T> result)
        {
            var work = Builder.Dense(Count);
            for (var i = 0; i < other.Count; i++)
            {
                DoMultiply(other.At(i), work);
                result.SetColumn(i, work);
            }
        }

        public static Matrix<T> OuterProduct(Vector<T> u, Vector<T> v)
        {
            return u.OuterProduct(v);
        }

        #endregion


        public T Maximum()
        {
            return At(MaximumIndex());
        }

        public virtual int MaximumIndex()
        {
            var index = 0;
            var max = At(index);
            for (var i = 1; i < Count; i++)
            {
                var test = At(i);

                if (NumOperators.GreaterThan(test, max))
                {
                    index = i;
                    max = test;
                }
            }

            return index;
        }

        public T Minimum()
        {
            return At(MinimumIndex());
        }
        public virtual int MinimumIndex()
        {
            var index = 0;
            var min = At(index);
            for (var i = 1; i < Count; i++)
            {
                var test = At(i);
                if (NumOperators.LessThan(test, min))
                {
                    index = i;
                    min = test;
                }
            }

            return index;
        }




        #region Pointwise Operations - 逐点操作

        public Vector<T> PointwiseAbs()
        {
            return PointwiseUnary(DoPointwiseAbs);
        }
        public void PointwiseAbs(Vector<T> result)
        {
            PointwiseUnary(DoPointwiseAbs, result);
        }


        /// <summary>
        /// 逐点将具有标量的绝对最大值应用于每个值。(?)
        /// Pointwise applies the absolute maximum with a scalar to each value.
        /// </summary>
        /// <param name="scalar">The scalar value to compare to.</param>
        public Vector<T> PointwiseAbsoluteMaximum(T scalar)
        {
            var result = Builder.SameAs(this);
            DoPointwiseAbsoluteMaximum(scalar, result);
            return result;
        }
        public void PointwiseAbsoluteMaximum(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoPointwiseAbsoluteMaximum(scalar, result);
        }
        public Vector<T> PointwiseAbsoluteMaximum(Vector<T> other)
        {
            var result = Builder.SameAs(this);
            DoPointwiseAbsoluteMaximum(other, result);
            return result;
        }
        public void PointwiseAbsoluteMaximum(Vector<T> other, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }
            DoPointwiseAbsoluteMaximum(other, result);
        }


        public Vector<T> PointwiseAbsoluteMinimum(T scalar)
        {
            var result = Builder.SameAs(this);
            DoPointwiseAbsoluteMinimum(scalar, result);
            return result;
        }
        public void PointwiseAbsoluteMinimum(T scalar, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoPointwiseAbsoluteMinimum(scalar, result);
        }
        public Vector<T> PointwiseAbsoluteMinimum(Vector<T> other)
        {
            var result = Builder.SameAs(this);
            DoPointwiseAbsoluteMinimum(other, result);
            return result;
        }
        public void PointwiseAbsoluteMinimum(Vector<T> other, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            DoPointwiseAbsoluteMinimum(other, result);
        }

        public Vector<T> PointwiseMultiply(Vector<T> other)
        {
            if (Count != other.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(other));
            }

            var result = Builder.SameAs(this, other);
            DoPointwiseMultiply(other, result);
            return result;
        }


        #endregion


        #region Pointwise Operations - 三角函数

        public Vector<T> PointwiseCos()
        {
            return PointwiseUnary(DoPointwiseCos);
        }

        protected virtual void DoPointwiseCos(Vector<T> result)
        {
            Map(NumOperators.Cos, result, Zeros.Include);
        }


        #endregion








        #region Virtual DoFunctions

        // 一元逐点操作
        private Vector<T> PointwiseUnary(Action<Vector<T>> fun)
        {
            var result = Builder.SameAs(this);
            fun(result);
            return result;
        }
        private void PointwiseUnary(Action<Vector<T>> f, Vector<T> result)
        {
            if (Count != result.Count)
            {
                throw new ArgumentException(Resources.Numerics.ArgumentVectorsSameLength, nameof(result));
            }

            f(result);
        }


        // 逐点绝对值
        protected virtual void DoPointwiseAbs(Vector<T> result)
        {
            Map(x => NumOperators.Abs(x), result, Zeros.AllowSkip);
        }

        // 逐点将具有标量的绝对最大值应用于每个值
        protected virtual void DoPointwiseAbsoluteMaximum(T scalar, Vector<T> result)
        {
            Map(x => NumOperators.Max(NumOperators.Abs(x), NumOperators.Abs(scalar)), result, Zeros.Include);
        }
        protected virtual void DoPointwiseAbsoluteMaximum(Vector<T> other, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Max(NumOperators.Abs(x), NumOperators.Abs(y)), other, result, Zeros.AllowSkip);
        }

        protected virtual void DoPointwiseAbsoluteMinimum(T scalar, Vector<T> result)
        {
            Map(x => NumOperators.Min(NumOperators.Abs(x), NumOperators.Abs(scalar)), result, Zeros.Include);
        }
        protected virtual void DoPointwiseAbsoluteMinimum(Vector<T> other, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Min(NumOperators.Abs(x), NumOperators.Abs(y)), other, result, Zeros.AllowSkip);
        }





        // 逐点乘
        protected virtual void DoPointwiseMultiply(Vector<T> other, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Multiply(x, y), other, result, Zeros.AllowSkip);
        }

        // 逐点除
        protected virtual void DoPointwiseDivide(Vector<T> divisor, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Divide(x, y), divisor, result, Zeros.AllowSkip);
        }

        // 逐点模
        protected virtual void DoPointwiseModulus(Vector<T> divisor, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Modulo(x, y), divisor, result, Zeros.AllowSkip);
        }

        // 逐点余
        protected virtual void DoPointwiseRemainder(Vector<T> divisor, Vector<T> result)
        {
            Map2((x, y) => NumOperators.Modulo(x, y), divisor, result, Zeros.AllowSkip);
        }





        #endregion


        //}}@@@
    }


}

